#pragma once

#include <xtd/obj_fun.h>

namespace My {

class Message {
public:
	static unsigned const Id = WM_APP + 0;
public:
	virtual ~Message() {}
	virtual void Execute() = 0;
	#pragma warning (push)
	#pragma warning(disable: 4311) // pointer truncation from 'My::Message *const ' to 'UINT'
	void PostMe()
		{ ::PostMessage(AfxGetApp()->m_pMainWnd->m_hWnd, Id, reinterpret_cast<UINT>(this), 0); }
	#pragma warning (pop)
};

//////////////////
// no arguments //
//////////////////

template <class T, class R>
class AsyncCallMessage : public Message
{
public:
	AsyncCallMessage(T & obj, R (T::*fun)())
		: obj_fun(obj, fun) { }
	virtual void Execute()
		{ obj_fun(); }
private:
	xtd::obj_fun_t<T, R> obj_fun;
};

template <class T, class R>
inline void AsyncCall(T & obj, R (T::*fun)())
	{ (new AsyncCallMessage<T,R>(obj, fun))->PostMe(); }

////////////////
// 1 argument //
////////////////

template <class T, class R, class A>
class AsyncCall1Message : public Message
{
public:
	AsyncCall1Message(T & obj, R (T::*fun)(A), A a)
		: obj_fun(obj, fun), arg(a) { }
	virtual void Execute()
		{ obj_fun(arg); }
private:
	xtd::obj_fun1_t<T, R, A> obj_fun;
	A arg;
};

template <class T, class R, class A>
inline void AsyncCall(T & obj, R (T::*fun)(A), A a)
	{ (new AsyncCall1Message<T,R,A>(obj, fun, a))->PostMe(); }

/////////////////
// 2 arguments //
/////////////////

template <class T, class R, class A1, class A2>
class AsyncCall2Message : public Message
{
public:
	AsyncCall2Message(T & obj, R (T::*fun)(A1, A2), A1 a1, A2 a2)
		: obj_fun(obj, fun), arg1(a1), arg2(a2) { }
	virtual void Execute()
		{ obj_fun(arg1, arg2); }
private:
	xtd::obj_fun2_t<T, R, A1, A2> obj_fun;
	A1 arg1;
	A2 arg2;
};

template <class T, class R, class A1, class A2>
inline void AsyncCall(T & obj, R (T::*fun)(A1, A2), A1 a1, A2 a2)
	{ (new AsyncCall2Message<T,R,A1,A2>(obj, fun, a1, a2))->PostMe(); }

/////////////////////////////////
// async deletion of an object //
/////////////////////////////////
//(remember that DestroyWindow should be called from the same GUI thread as CreateWindow)

template <class T>
class AsyncDeleteMessage : public Message
{
public:
	AsyncDeleteMessage(T * obj)
		: obj_(obj) { }
	virtual void Execute()
		{ delete obj_; }
private:
	T * obj_;
};

template <class T>
inline void AsyncDelete(T * obj)
	{ (new AsyncDeleteMessage<T>(obj))->PostMe(); }

} //namespace My
